/*
 *  ChartWindow.java 
 *    
 *    Copyright 2012 YAWP Project Team
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.googlecode.yawp.view;

import com.googlecode.yawp.model.Model;
import com.googlecode.yawp.model.Preferences;
import com.googlecode.yawp.model.WeightEntry;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Calendar;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import java.awt.BorderLayout;

import javax.swing.ButtonGroup;
import javax.swing.JRadioButton;
import javax.swing.JComboBox;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;

/**
 * ChartWindow used to chart user weight
 * <BR>
 * The ChartWindow uses the Observer Design Pattern and is an observer of the model.
 * Users may select modifications to the view but there is
 * no controller update as nothing in the model will need changing.
 * If the model is changed (by something like the AddWeight window the model will notify that 
 * the chart view needs to update.
 * 
 * <BR>
 * Requires the JFreeChart library
 * <BR>
 * <BR><B>Notes:</B><BR>
 * - main is used for stand alone testing only<BR>
 * - caller will pass model<BR>
 * - does ChartWindow need to unregister with model before exit?<BR>
 * - update needs data from model - sample data could be moved to
 *   the empty constructor for stand alone testing.<BR>
 * - currentMonth, currentYear should be collections of available months and years<BR>
 * - view modifier hooks needed for new month, new year, radio button changes (all should just generate an update call)<BR>
 *
 *<BR><B>Edit History:</B>
 * <BR>120314 - Initial Program with comments
 * 
 * @author ccato (ccato@bu.edu)
 * 
 */
@SuppressWarnings("serial")
public class ChartWindow extends JFrame implements Observer, WindowListener  { 
	
	private XYSeries series;
	private String seriesTitle;
	private String chartTitle;
	private String xAxisLabel;
	private String yAxisLabel;
	private int daysInMonth;
	private String currentMonth;
	private String currentYear;
	private JRadioButton rdbtnChange;
	private JRadioButton rdbtnWeight;
	
	private JComboBox cbMonth;
	private JComboBox cbYear;
	
	private String[] monthName = {"January", "February","March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
	//private String[] unitName = { "LBS", "KGS" };
	
	private ChartPanel chartPanel;
	
	private Model model = null;

	/**
	 * Constructor that creates a ChartWindow
	 * @param model Reference to the model for this application
	 */
	public ChartWindow(Model model) {
		this.model = model;
		model.addObserver(this);
		initialize();
		update(null, null);
	}

	/**
	 * Initialize the window
	 */
	private void initialize() {
       
		setTitle("YAWP - Weight Chart");
		
		// exit window on close
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        addWindowListener(this);
        
		setBounds(100, 100, 450, 500);
		new JPanel();

		// set current time for list boxes
		// TODO but this need to work with the filled data for cbMonth & cbYear!
		setCurrentTime();

				
		JPanel topPanel = new JPanel();
		getContentPane().add(topPanel, BorderLayout.NORTH);
		
		ButtonGroup group = new ButtonGroup();
		rdbtnWeight = new JRadioButton("Weight");
		rdbtnWeight.setSelected(true);
		group.add(rdbtnWeight);
		topPanel.add(rdbtnWeight);
		rdbtnWeight.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				System.out.println("Switch to Weight Mode and call Update");
				update(null, null);
			}
			
		});
		
		rdbtnChange = new JRadioButton("Change");
		group.add(rdbtnChange);
		topPanel.add(rdbtnChange);
		rdbtnChange.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				System.out.println("Switch to Weight Change Mode and call Update");
				update(null, null);
			}
			
		});
		
		cbMonth = new JComboBox();
		cbMonth.setModel(new DefaultComboBoxModel(monthName));
		// TODO check this if we switch to zero base in the model
		cbMonth.setSelectedIndex(model.getCurrentMonth() - 1);
		// TODO Need to select the current month as the default here
		topPanel.add(cbMonth);
		cbMonth.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
//				System.out.println("Switch to Months and call Update");
				update(null, null);
			}
			
		});
		
		cbYear = new JComboBox();
		// TODO Need to Add the users range of years to fill the combo box (do oldest year in DB to current to fill)
		cbYear.setModel(new DefaultComboBoxModel());
		// TODO Need to select current year as default starting point here
		topPanel.add(cbYear);
		cbYear.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				System.out.println("Switch to Years and call Update");
				update(null, null);
			}
			
		});
		
		JPanel bottomPanel = new JPanel();
		getContentPane().add(bottomPanel, BorderLayout.SOUTH);
		
		JButton btnClose = new JButton("Close");
		bottomPanel.add(btnClose);
		btnClose.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				unregister();
				dispose();
			}
			
		});
		
        setSize(500,350);
        setLocation(200,200);
        setVisible(true);
	}

	/**
	 * Render the chart panel
	 */
	private void renderChartPanel() {
		// if this is the first time make sure we go get the users data to plot first
		if (chartPanel != null) {
			remove(chartPanel);
		}
		
		// Add the series to your data set 
		XYSeriesCollection dataset = new XYSeriesCollection(); 
		dataset.addSeries(series);	
		
		// Generate the graph 
		JFreeChart chart = ChartFactory.createXYLineChart(
				chartTitle, 				// Title 
				xAxisLabel, 				// x-axis Label 
				yAxisLabel, 				// y-axis Label 
				dataset, 					// Dataset 
				PlotOrientation.VERTICAL, 	// Plot Orientation 
				true,						// Show Legend 
				true, 						// Use tooltips 
				false 						// Configure chart to generate URLs?
				);
		
		// turn on off lines and shapes...
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        renderer.setSeriesLinesVisible(0, true);
        renderer.setSeriesShapesVisible(0, true);

        // override the default scale
        XYPlot plot	= chart.getXYPlot();
        NumberAxis domainAxis = new NumberAxis(xAxisLabel);
        domainAxis.setRange(1, daysInMonth);
	
        // turn off .0 on the axis
        domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        plot.setDomainAxis(domainAxis);
        plot.setRenderer(renderer);
	
        // create a the chart panel from the chart
        chartPanel = new ChartPanel(chart);
        chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
	
        System.out.println("Added chartPanel");

        // update the window, need validate with repaint - not sure why but it works...
        // these could use getContentPane(). but they seem to work without it
		add(chartPanel);
		validate();
		repaint();

	}

	/**
	 * Update notification from model that there has been a change
	 * 
	 */
	@Override
	public void update(Observable o, Object arg) {
		
		int month;
		int year;
		
		String tempSeriesTitle = "" + model.getUserName() + "'s Weight";
		if(!tempSeriesTitle.equals(seriesTitle)) {
			// looks like we changed users so delete the years control items so we get new ones
			cbYear.removeAllItems();
		}
		
		// get series title
		seriesTitle = "" + model.getUserName() + "'s Weight";

		// the control should default to the current month and then what ever the user is looking for... 
		month = Integer.valueOf(cbMonth.getSelectedIndex());

		if(cbYear.getItemCount() == 0) {
			
			Calendar earliestDate = model.getEarliestDate();
			
			if (earliestDate == null) {
				return;
			}
			// populate year control with years starting with oldest date entry
			for (int i = earliestDate.get(Calendar.YEAR); i <= Model.getCurrentYear(); i++) {
				cbYear.addItem(i);
			}
			cbYear.setSelectedIndex(cbYear.getItemCount() - 1);
		}
		year = Integer.valueOf(cbYear.getSelectedItem().toString());
		
		// get series data
		// TODO need to add support for weight changes, check control, and get that vs Weights...
		if(rdbtnWeight.isSelected()) {
			// chart weights
			List<WeightEntry> weights = model.getMonthWeights(month + 1, year);
			series = new XYSeries(seriesTitle);
			for(WeightEntry weightEntry : weights) {
				series.add(weightEntry.getDay(), weightEntry.getWeight());		
				System.out.println("Weight - Day: " + weightEntry.getDay() + " Weight:" + weightEntry.getWeight());
			}
		} else {
			// chart changes
			List<WeightEntry> weights = model.getMonthWeights(month + 1, year);
			series = new XYSeries(seriesTitle);
			int counter = 1;
			double lastWeight = 0.0;
			double change = 0.0;
			for(WeightEntry weightEntry : weights) {
				if (counter == 1) {
					// first entry
					lastWeight = weightEntry.getWeight();
					counter++;
				} else {
					change = weightEntry.getWeight() - lastWeight;
					series.add(weightEntry.getDay(), change);	
					lastWeight = weightEntry.getWeight();
					System.out.println("Change - Day: " + weightEntry.getDay() + " Weight:" + weightEntry.getWeight() + " Last:" + lastWeight + " Change:" + change);
					counter++;
				}
			}
		}
		
		// get chart title
		if(rdbtnWeight.isSelected()) {
			chartTitle = "Weight for " + monthName[month];
		} else {
			chartTitle = "Change in Weight for " + monthName[month];			
		}
		
		// get axis titles
		xAxisLabel = "Days";
		yAxisLabel = Preferences.getWeightUnitName(model.getUserUnits());

		// get days in month
		daysInMonth = model.getDaysOfMonth(month + 1, year);
		
		
		renderChartPanel();
	}

	/**
	 * Sets the current month and year
	 */
	public void setCurrentTime() {
		// get current month - set initially by call then by view modifier
		currentMonth = monthName[model.getCurrentMonth() - 1];
System.out.println("currentMonth: " + monthName[model.getCurrentMonth() - 1]);
		
		// get current year - set initially by call then by view modifier
		currentYear = Integer.toString(model.getCurrentYear());
System.out.println("currentYear: " + Integer.toString(model.getCurrentYear()));
	}
	
	/**
	 *  Disconnect observer notifications
	 */
	private void unregister() {
		if (model != null) { 
			model.deleteObserver(this);
		}
	}
	
	/**
	 * WindowLister concrete method
	 * for System (Close) functionality
	 * after unregistering window as an observer
	 * @param e Close Window WindowEvent
	 */
	@Override
	public void windowClosing(WindowEvent e) {		
		unregister();
		dispose();
	}

	/**
	 * WindowListner concrete method
	 */
	@Override
	public void windowActivated(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * WindowListner concrete method
	 */
	@Override
	public void windowClosed(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * WindowListner concrete method
	 */
	@Override
	public void windowDeactivated(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * WindowListner concrete method
	 */
	@Override
	public void windowDeiconified(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * WindowListner concrete method
	 */
	@Override
	public void windowIconified(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * WindowListner concrete method
	 */
	@Override
	public void windowOpened(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}
}
